/*
 * Copyright (C) 2024 HiHope Open Source Organization.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import utd from '@ohos.data.uniformTypeDescriptor';
import { Log } from './Log';
import { fileAccess, fileIo, userFileManager } from '@kit.CoreFileKit';
import { dataSharePredicates } from '@kit.ArkData';
import { Context } from '@kit.AbilityKit';

const TAG = 'FileUtil'

const MEDIA_URI_PARTS = 5;
const MEDIA_URI_AUTHORITY_INDEX = 0;
const MEDIA_URI_TYPE_INDEX = 1;
const MEDIA_URI_ID_INDEX = 2;
const MEDIA_URI_REAL_NAME_INDEX = 3;
const MEDIA_URI_DISPLAY_NAME_INDEX = 4;

interface Size {
  height: number,
  width: number
}

export interface FileInfo {
  title: string,
  uri: string,
  selected: boolean,
  icon?: PixelMap | Resource | string,
  width?: number,
  height?: number,
  size?: number,
  isVideo?: boolean,
  utd: string
}

class MediaUriInfo {
  authority: string = 'media';
  mediaType: string = '';
  fileId: number = 0;
  realName: string = '';
  displayName: string = '';
  suffix: string = '';

  constructor(uri: string) {
    if (uri.startsWith('file://')) {
      let tempStr = uri.substring('file://'.length);
      let parts = tempStr.split('/');
      if (parts.length == MEDIA_URI_PARTS && parts[MEDIA_URI_AUTHORITY_INDEX] == 'media') {
        this.mediaType = parts[MEDIA_URI_TYPE_INDEX];
        this.fileId = Number(parts[MEDIA_URI_ID_INDEX]);
        this.realName = parts[MEDIA_URI_REAL_NAME_INDEX];
        this.displayName = parts[MEDIA_URI_DISPLAY_NAME_INDEX];
        this.suffix = parts[MEDIA_URI_DISPLAY_NAME_INDEX].substring(parts[4].lastIndexOf('.'));
      }
    }
  }
}

export default class FileUtil {
  static isImage(typeId: string) {
    let typeDescriptor = utd.getTypeDescriptor(typeId)
    return typeDescriptor && typeDescriptor.belongsTo(utd.UniformDataType.IMAGE)
  }

  static isVideo(typeId: string) {
    let typeDescriptor = utd.getTypeDescriptor(typeId)
    return typeDescriptor && typeDescriptor.belongsTo(utd.UniformDataType.VIDEO)
  }

  static isDirectory(typeId: string): boolean {
    let typeDescriptor = utd.getTypeDescriptor(typeId)
    return typeDescriptor && typeDescriptor.belongsTo(utd.UniformDataType.DIRECTORY)
  }

  static isNormalFile(typeId: string) {
    !FileUtil.isDirectory(typeId) && !FileUtil.isImage(typeId) && !FileUtil.isVideo(typeId)
  }

  static parseSize(bytes: number): string {
    const units = ['B', 'K', 'M', 'G']
    let num: number = bytes
    for (let i = 0; i < 4; i++) {
      if (num < 1024) {
        return num.toFixed(i == 0 ? 0 : 3) + units[i]
      }
      num /= 1024
    }
    return (num * 1024).toFixed(3) + units[3]
  }

  static getThumbnail(sourceUri: string, size: Size): string {
    if (size.width != 0 && size.height != 0) {
      return `${sourceUri}?oper=thumbnail&width=${size.width}&height=${size.height}`;
    } else {
      Log.warn(TAG, 'getThumbnailSafe with width==0 and height==0, so do not use thumbnail' + JSON.stringify(size));
      return `${sourceUri}`;
    }
  }

  static async getMediaInfo(fileInfos: FileInfo[], context: Context) {
    let fileAssets: Array<userFileManager.FileAsset> = [];
    try {
      let predicates: dataSharePredicates.DataSharePredicates = new dataSharePredicates.DataSharePredicates()
        .in('file_id', fileInfos.map((fileInfo: FileInfo) => {
          return new MediaUriInfo(fileInfo.uri).fileId;
        }));
      let fetchOptions: userFileManager.FetchOptions = {
        fetchColumns: ['width', 'height', 'orientation', 'size'],
        predicates: predicates
      };

      let userFileMgr = userFileManager.getUserFileMgr(context);
      let fetchFileResult: userFileManager.FetchResult<userFileManager.FileAsset> = await userFileMgr.getPhotoAssets(fetchOptions);
      fileAssets = await fetchFileResult.getAllObject();

      const RIGHT_ANGLE = 90;
      const EVEN_DIVIDER = 2;
      const DEFAULT_THUMBNAIL_SIZE = 1;// 使用1而不是0作为默认尺寸,避免计算长宽比时除0异常
      fileInfos.forEach((fileInfo: FileInfo) => {
        let fileAsset: userFileManager.FileAsset | undefined =
          fileAssets.find((fileAsset: userFileManager.FileAsset) => {
            return fileAsset.uri == fileInfo.uri;
          });
        if (fileAsset) {
          let orientation: number = Number(fileAsset.get('orientation'));
          if ((orientation / RIGHT_ANGLE) % EVEN_DIVIDER == 0) {
            fileInfo.width = Number(fileAsset.get('width'));
            fileInfo.height = Number(fileAsset.get('height'));
          } else {
            fileInfo.width = Number(fileAsset.get('height'));
            fileInfo.height = Number(fileAsset.get('width'));
          }
        } else {
          fileInfo.width = DEFAULT_THUMBNAIL_SIZE;
          fileInfo.height = DEFAULT_THUMBNAIL_SIZE;
        }
        fileInfo.size = Number(fileAsset.get('size'));
      })
    } catch (err) {
      Log.info(TAG, `getMediaInfo err ${JSON.stringify(err)}`);
    }
  }

  static async getSubFilesInfo(dirInfo: FileInfo, context: Context): Promise<FileInfo[]> {
    let fileInfos: FileInfo[] = [];
    try {
      let fileAccessHelper = fileAccess.createFileAccessHelper(context);
      let dirFAFileInfo: fileAccess.FileInfo = await fileAccessHelper.getFileInfoFromUri(dirInfo.uri);
      let fileIterator: fileAccess.FileIterator = dirFAFileInfo.listFile();
      while (true) {
        let fileNext = fileIterator.next();
        if (fileNext.done) {
          break;
        }
        let fAFileInfo: fileAccess.FileInfo = fileNext.value;
        let stat: fileIo.Stat = fileIo.statSync(fAFileInfo.relativePath);
        if (!stat.isFile()) {
          continue;
        }

        fileInfos.push({
          title: fAFileInfo.fileName,
          uri: fAFileInfo.uri,
          icon: '',
          selected: true,
          size: stat.size,
          utd: utd.getUniformDataTypeByFilenameExtension(fAFileInfo.fileName.substring(fAFileInfo.fileName.lastIndexOf('.')))
        });
      }
    } catch (err) {
      Log.info(TAG, `getSubFilesInfo err ${JSON.stringify(err)}`);
    }
    return fileInfos;
  }

  static async getFileSizes(fileInfos: FileInfo[], context: Context) {
    try {
      let fileAccessHelper = fileAccess.createFileAccessHelper(context);
      for (let fileInfo of fileInfos) {
        let fAFileInfo: fileAccess.FileInfo = await fileAccessHelper.getFileInfoFromUri(fileInfo.uri);
        let stat: fileIo.Stat = fileIo.statSync(fAFileInfo.relativePath);
        fileInfo.size = stat.size;
      }
    } catch (err) {
      Log.info(TAG, `getFileSizes err ${JSON.stringify(err)}`);
    }
  }

  static parseTransferSpeed(speedBps: number): string {
    const ONE_KILO = 1024;
    const ONE_MEGA = 1024 * 1024;
    return speedBps  < ONE_MEGA ? ((speedBps  / ONE_KILO).toFixed(2) + 'KB/s') : ((speedBps  / ONE_MEGA).toFixed(2) + 'MB/s');
  }
}